﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using MyVocabulary.App_Code;
using MyVocabulary.Popup;

namespace MyVocabulary
{
    public partial class AddVerb : Form
    {
        #region Consts

        protected static class FormMessages
        {
            public const string ValidateVerbInfinitiveForm = "Please enter the verb's infinitive form";
            public const string ValidateTranslation = "Please enter a translation";

            public const string ErrorMessageTitle = "Error";
            public const string MultipleSelectionForEdit = "Please select only one item to edit.";

            public const string DeleteFormTitle = "Delete";
            public const string ConfirmItemRemovalQuestion = "Do you really want to remove the selected item(s) from the list?";
        }

        protected static class ConjugationBottomMenuTooltips
        {
            public const string Add = "Create a new item under the selected element in the list or at the end of the list if no selection is available";
            public const string Edit = "Edit the selected conjugation";
            public const string Delete = "Delete the selected conjugation(s)";
        }

        public enum AddVerbWindowMode
        {
            Add,
            Edit
        }

        #endregion

        #region Properties

        protected AddVerbWindowMode mode;
        /// <summary>
        /// Indicate if the form is in the add or edit mode.
        /// </summary>
        public AddVerbWindowMode Mode
        {
            get { return mode; }
            set
            {
                mode = value;
                Text = mode.ToString(); //Set the form title
            }
        }

        /// <summary>
        /// This object contains all the data that appears on the form.
        /// When the user presses the ok button all the values entered in the form are saved in this object.
        /// </summary>
        public Verb VocabVerb { get; set; }

        /// <summary>
        /// A list of existing tenses that will be used as a suggestion when adding a new verb conjugation
        /// </summary>
        public List<string> ExistingTenses { get; set; }

        /// <summary>
        /// The list of grammatical persons to use by default when adding new verb conjugations
        /// </summary>
        public List<Person> Persons { get; set; }

        /// <summary>
        /// Occurs after the user presses the 'OK' button and the data is saved into the VocabVerb property.
        /// </summary>
        public event EventHandler OnDataSaved;

        #endregion

        #region Constructors

        /// <summary>
        /// Create a new form
        /// </summary>
        /// <param name="Mode">Indicate if the form is in the add or edit mode.</param>
        /// <param name="OnDataSaved">Occurs after the user presses the 'OK' button and the data is saved into the VocabVerb property.</param>
        /// <param name="Persons">The list of grammatical persons to use by default when adding new verb conjugations</param>
        /// <param name="ExistingTenses">A list of existing tenses that will be used as a suggestion when adding a new verb conjugation</param>
        public AddVerb(AddVerbWindowMode Mode, EventHandler OnDataSaved, List<Person> Persons, List<string> ExistingTenses)
        {
            InitializeComponent();
            
            this.Mode = Mode;
            this.OnDataSaved = OnDataSaved;            
            this.Persons = Persons;
            this.ExistingTenses = ExistingTenses;
            VocabVerb = new Verb {Conjugations = new List<VerbConjugation>()};

            SetConjugationMenuButtons();
        }

        /// <summary>
        /// Create a new form
        /// </summary>
        /// <param name="Mode">Indicate if the form is in the add or edit mode.</param>
        /// <param name="OnDataSaved">Occurs after the user presses the 'OK' button and the data is saved into the VocabVerb property.</param>
        /// <param name="Persons">The list of grammatical persons to use by default when adding new verb conjugations</param>
        /// <param name="ExistingTenses">A list of existing tenses that will be used as a suggestion when adding a new verb conjugation</param>
        /// <param name="VocabVerb">This object contains all the data that appears on the form. 
        /// When the user presses the ok button all the values entered in the form are saved in this object.</param>
        public AddVerb(AddVerbWindowMode Mode, EventHandler OnDataSaved, List<Person> Persons, List<string> ExistingTenses, Verb VocabVerb)
        {
            InitializeComponent();

            this.Mode = Mode;
            this.OnDataSaved = OnDataSaved;         
            this.Persons = Persons;
            this.ExistingTenses = ExistingTenses;
            this.VocabVerb = VocabVerb;

            tbInfinitiveForm.Text = VocabVerb.InfinitiveForm;
            tbTranslation.Text = VocabVerb.Translation;            
            SetText(tbExamples, VocabVerb.Examples);
            SetText(tbRemarks, VocabVerb.Remarks);

            FillConjugationListView();
            SetConjugationMenuButtons();
        }

        #endregion

        #region TextBoxes

        /// <summary>
        /// Fix multiline textboxes that do not show new lines
        /// </summary>
        /// <param name="tb"></param>
        /// <param name="text"></param>
        protected static void SetText(TextBox tb, string text)
        {
            if(tb.Multiline)
            {
                tb.Lines = (new Regex(@"\n|\r\n").Split(text));                
            }
            else
            {
                tb.Text = text;                
            }
        }

        /// <summary>
        /// Check if the infinitive form TextBox is filled.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tbInfinitiveForm_Validating(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (string.IsNullOrEmpty(tbInfinitiveForm.Text.Trim()))
                errorProvider1.SetError(tbInfinitiveForm, FormMessages.ValidateVerbInfinitiveForm);
            else
                errorProvider1.SetError(tbInfinitiveForm, string.Empty);
        }

        /// <summary>
        /// Check if the translation TextBox is filled.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tbTranslation_Validating(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (string.IsNullOrEmpty(tbTranslation.Text.Trim()))
                errorProvider1.SetError(tbTranslation, FormMessages.ValidateTranslation);
            else
                errorProvider1.SetError(tbTranslation, string.Empty);
        }

        /// <summary>
        /// Enable ctrl + A in TextBoxes
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TextBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (!e.Control || e.KeyCode != Keys.A || !(sender is TextBox))
                return;

            var txt = (TextBox) sender;
            txt.SelectAll();
            e.Handled = true;
        }

        #endregion
        
        #region Conjugations

        private void FillConjugationListView()
        {
            lvConjugations.Items.Clear();
            foreach (var conjugation in VocabVerb.Conjugations)
            {
                var item = new CustomListViewItem(conjugation.Tense);
                item.AdditionalInfo[VocabularyVerbConst.VerbConjugation] = conjugation;
                lvConjugations.Items.Add(item);
            }
        }

        private void lvConjugations_SelectedIndexChanged(object sender, EventArgs e)
        {
            SetConjugationMenuButtons();
        }

        private void lvConjugations_DoubleClick(object sender, EventArgs e)
        {
            btnConjugationEdit_Click(sender, e);
        }

        private void cmConjugations_Opening(object sender, System.ComponentModel.CancelEventArgs e)
        {
            cmConjugations.Items["btnConjugationEdit"].Visible =
                cmConjugations.Items["btnConjugationDelete"].Visible = lvConjugations.SelectedItems.Count > 0;
        }

        #region Conjugation list bottom menu buttons

        /// <summary>
        /// Set the buttons that appear at the buttom of the conjugations ListView
        /// </summary>
        protected void SetConjugationMenuButtons()
        {
            if (lvConjugations.SelectedItems.Count == 0)
            {
                btnConjugationMenu_1.Visible = true;
                SetAddConjugationButton(btnConjugationMenu_1);

                btnConjugationMenu_2.Visible = false;

                btnConjugationMenu_3.Visible = false;

                return;
            }

            btnConjugationMenu_1.Visible = true;
            SetDeleteConjugationButton(btnConjugationMenu_1);

            btnConjugationMenu_2.Visible = true;
            SetEditConjugationButton(btnConjugationMenu_2);

            btnConjugationMenu_3.Visible = true;
            SetAddConjugationButton(btnConjugationMenu_3);
        }

        protected void SetAddConjugationButton(Button btn)
        {
            btn.Image = Properties.Resources.Add;
            toolTip1.SetToolTip(btn, ConjugationBottomMenuTooltips.Add);
            SetConjugationOnClick(btn, btnConjugationAdd_Click);
        }

        protected void SetEditConjugationButton(Button btn)
        {
            btn.Image = Properties.Resources.Edit;
            toolTip1.SetToolTip(btn, ConjugationBottomMenuTooltips.Edit);
            SetConjugationOnClick(btn, btnConjugationEdit_Click);
        }

        protected void SetDeleteConjugationButton(Button btn)
        {
            btn.Image = Properties.Resources.Delete;
            toolTip1.SetToolTip(btn, ConjugationBottomMenuTooltips.Delete);
            SetConjugationOnClick(btn, btnConjugationDelete_Click);
        }

        protected void SetConjugationOnClick(Button btn, EventHandler method)
        {
            btn.Click -= btnConjugationAdd_Click;
            btn.Click -= btnConjugationEdit_Click;
            btn.Click -= btnConjugationDelete_Click;

            btn.Click += method;
        }

        #endregion

        #region Conjugation Commands

        private void btnConjugationAdd_Click(object sender, EventArgs e)
        {
            var form = new AbbVerbConjugation(AbbVerbConjugation.AbbVerbConjugationMode.Add, ConjugationOnDataSaved, Persons, ExistingTenses);
            form.ShowDialog(this);
        }

        private void btnConjugationEdit_Click(object sender, EventArgs e)
        {
            if (lvConjugations.SelectedItems.Count == 0)
                return;

            if (lvConjugations.SelectedItems.Count > 1)
            {
                (new CustomMessageBox(FormMessages.MultipleSelectionForEdit, FormMessages.ErrorMessageTitle,
                                      CustomMessageBoxButtons.Cancel)).ShowDialog();
                return;
            }

            var item = (CustomListViewItem)lvConjugations.SelectedItems[0];
            var conjugation = (VerbConjugation)item.AdditionalInfo[VocabularyVerbConst.VerbConjugation];
            
            var form = new AbbVerbConjugation(AbbVerbConjugation.AbbVerbConjugationMode.Edit, ConjugationOnDataSaved, Persons, ExistingTenses, conjugation);
            form.ShowDialog(this);
        }

        public void ConjugationOnDataSaved(object sender, EventArgs e)
        {
            if (sender == null || !(sender is AbbVerbConjugation))
                return;

            var form = sender as AbbVerbConjugation;
            switch (form.Mode)
            {
                case AbbVerbConjugation.AbbVerbConjugationMode.Add:
                    var item = new CustomListViewItem(form.VerbConjugation.Tense);
                    item.AdditionalInfo[VocabularyVerbConst.VerbConjugation] = form.VerbConjugation;
                    if (lvConjugations.SelectedItems.Count == 0)
                        lvConjugations.Items.Add(item); //Add on the last position
                    else
                        lvConjugations.Items.Insert(lvConjugations.SelectedItems[0].Index + 1, item); //Add after the first selected item
                    item.EnsureVisible();
                    break;
                case AbbVerbConjugation.AbbVerbConjugationMode.Edit:
                    var editItem = (CustomListViewItem)lvConjugations.SelectedItems[0];
                    editItem.Text = form.VerbConjugation.Tense;                    
                    editItem.AdditionalInfo[VocabularyVerbConst.VerbConjugation] = form.VerbConjugation;
                    break;
            }          
        }

        private void btnConjugationDelete_Click(object sender, EventArgs e)
        {
            if (lvConjugations.SelectedItems.Count == 0)
                return;

            if ((new CustomMessageBox(FormMessages.ConfirmItemRemovalQuestion, FormMessages.DeleteFormTitle, CustomMessageBoxButtons.YesNo)).ShowDialog() == DialogResult.Yes)
            {
                foreach (ListViewItem item in lvConjugations.SelectedItems)
                {
                    lvConjugations.Items.Remove(item);
                }
            }
        }

        #endregion

        #region Drag&Drop

        private void lvConjugations_ItemDrag(object sender, ItemDragEventArgs e)
        {
            lvConjugations.DoDragDrop("lvConjugations", DragDropEffects.Move);
        }

        private void lvConjugations_DragEnter(object sender, DragEventArgs e)
        {
            if (!e.Data.GetDataPresent(DataFormats.Text))
            {
                e.Effect = DragDropEffects.None;
                return;
            }
            var text = (string)e.Data.GetData(typeof(string));
            e.Effect = text.CompareTo("lvConjugations") == 0 ? DragDropEffects.Move : DragDropEffects.None;
        }

        private void lvConjugations_DragOver(object sender, DragEventArgs e)
        {
            if (!e.Data.GetDataPresent(DataFormats.Text))
            {
                e.Effect = DragDropEffects.None;
                return;
            }
            var cp = lvConjugations.PointToClient(new Point(e.X, e.Y));
            var hoverItem = lvConjugations.GetItemAt(cp.X, cp.Y);
            if (hoverItem == null)
            {
                e.Effect = DragDropEffects.None;
                return;
            }
            foreach (CustomListViewItem moveItem in lvConjugations.SelectedItems)
            {
                if (moveItem.Index == hoverItem.Index)
                {
                    e.Effect = DragDropEffects.None;
                    hoverItem.EnsureVisible();
                    return;
                }
            }
            var text = (String)e.Data.GetData("lvConjugations".GetType());
            if (text.CompareTo("lvConjugations") == 0)
            {
                e.Effect = DragDropEffects.Move;
                hoverItem.EnsureVisible();
            }
            else
            {
                e.Effect = DragDropEffects.None;
            }
        }

        private void lvConjugations_DragDrop(object sender, DragEventArgs e)
        {
            if (lvConjugations.SelectedItems.Count == 0)
            {
                return;
            }
            var cp = lvConjugations.PointToClient(new Point(e.X, e.Y));
            var dragToItem = lvConjugations.GetItemAt(cp.X, cp.Y);
            if (dragToItem == null)
            {
                return;
            }
            var dropIndex = dragToItem.Index;
            if (dropIndex > lvConjugations.SelectedItems[0].Index)
            {
                dropIndex++;
            }
            var insertItems = new ArrayList(lvConjugations.SelectedItems.Count);
            foreach (CustomListViewItem item in lvConjugations.SelectedItems)
            {
                var newItem = (CustomListViewItem)item.Clone();
                newItem.AdditionalInfo = item.AdditionalInfo;
                insertItems.Add(newItem);
            }
            for (var i = insertItems.Count - 1; i >= 0; i--)
            {
                var insertItem = (CustomListViewItem)insertItems[i];
                lvConjugations.Items.Insert(dropIndex, insertItem);
            }
            foreach (CustomListViewItem removeItem in lvConjugations.SelectedItems)
            {
                lvConjugations.Items.Remove(removeItem);
            }
        }

        #endregion

        #endregion

        #region Form Shortcuts & Buttons

        /// <summary>
        /// Enable form shortcuts:
        /// * Ctrl + S - Save the form
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void AddWord_KeyDown(object sender, KeyEventArgs e)
        {
            if (!e.Control) return;

            switch (e.KeyCode)
            {
                case Keys.S:
                    e.Handled = true;
                    e.SuppressKeyPress = true;
                    btnOK_Click(sender, e);
                    break;
            }
        }

        /// <summary>
        /// Save the form data into the VocabVerb property and call the OnDataSaved event handler.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnOK_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(tbInfinitiveForm.Text.Trim()) || string.IsNullOrEmpty(tbTranslation.Text.Trim()))
                return;

            VocabVerb.InfinitiveForm = tbInfinitiveForm.Text;
            VocabVerb.Translation = tbTranslation.Text;
            VocabVerb.Examples = tbExamples.Text;
            VocabVerb.Remarks = tbRemarks.Text;

            VocabVerb.Conjugations.Clear();
            foreach (CustomListViewItem item in lvConjugations.Items)
            {
                var conjugation = (VerbConjugation) item.AdditionalInfo[VocabularyVerbConst.VerbConjugation];
                VocabVerb.Conjugations.Add(conjugation);
            }

            if (OnDataSaved != null)
                OnDataSaved(this, null);

            Close();
        }

        /// <summary>
        /// Close the form
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCancel_Click(object sender, EventArgs e)
        {
            Close();
        }

        #endregion
    }
}
